#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/mutex.h>

#include <mach/pinctrl.h>    


// #define PIN_REDLASER     MXS_PIN_ENCODE(2, 4)
#define PIN_REDLASER     MXS_PIN_ENCODE(3, 26)

#define MYGPIO MXS_PIN_TO_GPIO(PIN_REDLASER)

struct redlaser_data {
    #define MODE_ON    (1)
    #define MODE_OFF   (0)
    #define MODE_2Hz   (2)
    #define MODE_200Hz (200)
	int mode;
    #define DO_RUN    (1)
    #define DO_STOP    (2)
    int run;
    struct mutex lock;
    struct timer_list timer;
    struct work_struct work;

    int deta;
    int gpio;
};




static ssize_t redlaser_show(
    struct device *dev,
    struct device_attribute *attr,
    char *buf)
{
	struct redlaser_data *pdata = (struct redlaser_data *)dev->platform_data;

	return snprintf(buf, PAGE_SIZE, "%d\n", pdata->mode);
}


void do_set_gpio(struct redlaser_data *pdata, int value)
{
    // int ret = gpio_request(MYGPIO, "abc");

    // if (ret != 0) {
    //     printk("error %s %d gpio_request\r\n ", __FUNCTION__, __LINE__);
    // }
    // else {
        // TODO 没有判断ret返回值
        gpio_set_value(MYGPIO, value);
        // gpio_free(MYGPIO);
    // }

}

static ssize_t redlaser_store(
    struct device *dev,
    struct device_attribute *attr,
    const char *buf,
    size_t count)
{
	struct redlaser_data *pdata = (struct redlaser_data *)dev->platform_data;
	char *after;

	pdata->mode = simple_strtoul(buf, &after, 10);
    switch(pdata->mode) {
    case MODE_ON:
        pdata->deta = 5;
        pdata->run = DO_RUN;
        break;
    case MODE_OFF:
        pdata->deta = 0;
        pdata->run = DO_STOP;
        do_set_gpio(pdata, 0);
        break;
    case MODE_2Hz:
        pdata->deta = 500;
        pdata->run = DO_RUN;
        break;
    }

    if (pdata->deta) {
        mod_timer(&pdata->timer, jiffies + msecs_to_jiffies(pdata->deta));
    }
	return count;
}

static DEVICE_ATTR(
    redlaser, S_IWUSR | S_IRUGO, redlaser_show, redlaser_store);


static struct miscdevice *pmisc;


static void gpio_keys_timer(unsigned long _data)
{
    struct redlaser_data *pdata = (struct redlaser_data *)_data;
    static int times = 0;
    static int state = 0;

    
    // printk("run %x\r\n", pdata->run);
    if (pdata->run != DO_RUN) {
        return;
    }

    // int ret = gpio_request(MYGPIO, "abc");
    // printk("ret = %d\r\n", ret);
    // ret = gpio_request(MYGPIO, "abc");
    // printk("retry ret = %d\r\n", ret);
    if (state == 0 ) {
        // gpio_set_value(MYGPIO, 1);
        do_set_gpio(pdata, 1);
        state = 1;
    }
    else {
        // gpio_set_value(MYGPIO, 0);
        do_set_gpio(pdata, 0);
        state = 0;
    }
    
   if (pdata->deta) {
        mod_timer(&pdata->timer, jiffies + msecs_to_jiffies(pdata->deta));
   }
}

static void gpio_keys_work_func(struct work_struct *work)   
{
    struct redlaser_data *pdata =  container_of(work, struct redlaser_data, work); 

    printk("ptr %x", pdata);

    
    mod_timer(&pdata->timer, jiffies + msecs_to_jiffies(1000));
    
}


static int __devinit mxs_hsadc_probe(struct platform_device *pdev)
{
	int                    i, ret;
	char                   name[16];
	struct device         *this_dev;
	struct miscdevice     *tpmisc;
	struct redlaser_data *pdata, *tpdata;

    struct redlaser_data *npdata = pdev->dev.platform_data;   
    
	pmisc = kzalloc(sizeof(struct miscdevice),    GFP_KERNEL);
	pdata = kzalloc(sizeof(struct redlaser_data), GFP_KERNEL);

	tpmisc = pmisc, tpdata = pdata;

    snprintf(name, sizeof(name), "redlaser%d", i);
    tpmisc->name  = name;
    tpmisc->minor = MISC_DYNAMIC_MINOR;
    tpmisc->fops  = NULL;
    misc_register(tpmisc);

    this_dev                = tpmisc->this_device;
    this_dev->platform_data = tpdata;

    tpdata->mode         = MODE_ON;
    tpdata->run = DO_RUN;
    tpdata->deta = 0;
    tpdata->gpio = MXS_PIN_TO_GPIO(PIN_REDLASER);
    device_create_file(this_dev, &dev_attr_redlaser);
    
    ret = gpio_request(MYGPIO, "abc");
    if (ret != 0) {
        printk("error %s %d gpio_request\r\n ", __FUNCTION__, __LINE__);
    }
    else {
        gpio_direction_output(MYGPIO, 0);// 输出低电平
        // gpio_free(MYGPIO);
    }
    // device_create_file(npdata, &dev_attr_redlaser);

    

    printk("addr in %x\r\n", tpdata);


    

    setup_timer(&tpdata->timer, gpio_keys_timer, tpdata);
    // INIT_WORK(&tpdata->work, gpio_keys_work_func);
    
    // mod_timer(&tpdata->timer, jiffies + msecs_to_jiffies(tpdata->deta));
    // int a;
    return 0;
}


static int __devexit redlaser_remove(struct platform_device *pdev)
{
	int i;
	struct miscdevice *tpmisc;
	struct redlaser_data *pdata;

    tpmisc = pmisc;
    misc_deregister(tpmisc);

    // printk("remove %x %x %x\r\n", tpmisc->this_device->platform_data,
    //         tpmisc->this_device,
    //         tpmisc);

    pdata = tpmisc->this_device->platform_data;
    pdata->run = DO_STOP;
    tpmisc->this_device->platform_data = NULL;
    tpmisc->this_device = NULL;
    gpio_free(MYGPIO);
    kfree(pmisc);
    tpmisc =  NULL;
    
}


static struct platform_driver mxs_hsadc_driver = {
	.probe		= mxs_hsadc_probe,
	.remove		= __exit_p(redlaser_remove),
	// .suspend	= mxs_hsadc_suspend,
	// .resume		= mxs_hsadc_resume,
	.driver		= {
		.name   = "redlaser",
		.owner	= THIS_MODULE,
	},
};

int __init redlaser_init(void)
{
    platform_driver_register(&mxs_hsadc_driver);
}
static void __exit redlaser_exit(void)
{
	platform_driver_unregister(&mxs_hsadc_driver);
}
module_init(redlaser_init);
module_exit(redlaser_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("glink devices user space export");